/*
 * Copyright(c) 2007 the Spark project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */

package org.libspark.swfassist.inprogress.swf
{
	import org.libspark.swfassist.swf.actions.ActionRecord;
	import org.libspark.swfassist.swf.actions.ActionRecords;
	import org.libspark.swfassist.swf.filters.Filter;
	import org.libspark.swfassist.swf.filters.FilterList;
	import org.libspark.swfassist.swf.structures.ButtonCondAction;
	import org.libspark.swfassist.swf.structures.ButtonRecord;
	import org.libspark.swfassist.swf.structures.CXForm;
	import org.libspark.swfassist.swf.structures.CXFormWithAlpha;
	import org.libspark.swfassist.swf.structures.CurvedEdgeRecord;
	import org.libspark.swfassist.swf.structures.FillStyle;
	import org.libspark.swfassist.swf.structures.FillStyleArray;
	import org.libspark.swfassist.swf.structures.GlyphEntry;
	import org.libspark.swfassist.swf.structures.LineStyle;
	import org.libspark.swfassist.swf.structures.LineStyle2;
	import org.libspark.swfassist.swf.structures.LineStyleArray;
	import org.libspark.swfassist.swf.structures.Matrix;
	import org.libspark.swfassist.swf.structures.MorphFillStyle;
	import org.libspark.swfassist.swf.structures.MorphFillStyleArray;
	import org.libspark.swfassist.swf.structures.MorphGradient;
	import org.libspark.swfassist.swf.structures.MorphGradientRecord;
	import org.libspark.swfassist.swf.structures.MorphLineStyle;
	import org.libspark.swfassist.swf.structures.MorphLineStyles;
	import org.libspark.swfassist.swf.structures.RGBA;
	import org.libspark.swfassist.swf.structures.Rect;
	import org.libspark.swfassist.swf.structures.Shape;
	import org.libspark.swfassist.swf.structures.ShapeRecord;
	import org.libspark.swfassist.swf.structures.ShapeRecordTypeConstants;
	import org.libspark.swfassist.swf.structures.ShapeWithStyle;
	import org.libspark.swfassist.swf.structures.StraightEdgeRecord;
	import org.libspark.swfassist.swf.structures.StyleChangeRecord;
	import org.libspark.swfassist.swf.structures.TextRecord;
	import org.libspark.swfassist.swf.tags.AbstractTagVisitor;
	import org.libspark.swfassist.swf.tags.DefineButton2;
	import org.libspark.swfassist.swf.tags.DefineFont2;
	import org.libspark.swfassist.swf.tags.DefineFont3;
	import org.libspark.swfassist.swf.tags.DefineMorphShape;
	import org.libspark.swfassist.swf.tags.DefineShape;
	import org.libspark.swfassist.swf.tags.DefineShape2;
	import org.libspark.swfassist.swf.tags.DefineShape3;
	import org.libspark.swfassist.swf.tags.DefineShape4;
	import org.libspark.swfassist.swf.tags.DefineText;
	import org.libspark.swfassist.swf.tags.PlaceObject2;
	import org.libspark.swfassist.swf.tags.PlaceObject3;
	import org.libspark.swfassist.swf.tags.RemoveObject2;
	import org.libspark.swfassist.swf.tags.DefineSprite;
	
	public class SWFPrinter extends AbstractTagVisitor
	{
		protected function print(...messages:Array):void
		{
			messages[0] = indentValue + messages[0];
			trace.apply(null, messages);
		}
		
		protected function indent():void
		{
			indentValue += '  ';
		}
		
		protected function unindent():void
		{
			indentValue = indentValue.substr(0, -2);
		}
		
		protected var indentValue:String = '';
		
		public override function visitPlaceObject2(tag:PlaceObject2):void
		{
			// return;
			print('<PlaceObject2' + (tag.hasCharacter ? ' id="' + tag.characterId : '') + '" depth="' + tag.depth + '" move="' + tag.isMove + '">');
			indent();
			if (tag.hasMatrix) {
				visitMatrix(tag.matrix);
			}
			if (tag.hasColorTransform) {
				visitCXForm(tag.colorTransform);
			}
			unindent();
			print('</PlaceObject2>');
		}
		
		public override function visitPlaceObject3(tag:PlaceObject3):void
		{
			// return;
			print('<PlaceObject3' + (tag.hasCharacter ? ' id="' + tag.characterId : '') + '" depth="' + tag.depth + '" move="' + tag.isMove + '">');
			indent();
			if (tag.hasMatrix) {
				visitMatrix(tag.matrix);
			}
			if (tag.hasColorTransform) {
				visitCXForm(tag.colorTransform);
			}
			if (tag.hasFilterList) {
				visitFilterList(tag.filterList);
			}
			unindent();
			print('</PlaceObject3>');
		}
		
		public function visitFilterList(list:FilterList):void
		{
			print('<FilterList>');
			indent();
			for each (var filter:Filter in list.filters) {
				print('<Filter type="' + filter.filterId + '"/>');
			}
			unindent();
			print('</FilterList>');
		}
		
		public override function visitRemoveObject2(tag:RemoveObject2):void
		{
			// return;
			print('<RemoveObject2 depth="' + tag.depth + '">');
		}
		
		public override function visitDefineShape(tag:DefineShape):void
		{
			// return;
			print('<DefineShape>');
			indent();
			visitShape(tag.shapes);
			unindent();
			print('</DefineShape>');
		}
		
		public override function visitDefineShape2(tag:DefineShape2):void
		{
			// return;
			print('<DefineShape2>');
			indent();
			visitShape(tag.shapes);
			unindent();
			print('</DefineShape3>');
		}
		
		public override function visitDefineShape3(tag:DefineShape3):void
		{
			// return;
			print('<DefineShape3>');
			indent();
			visitShape(tag.shapes);
			unindent();
			print('</DefineShape3>');
		}
		
		public override function visitDefineShape4(tag:DefineShape4):void
		{
			// return;
			print('<DefineShape4 id="' + tag.shapeId + '" useNonScalingStrokes="' + tag.useNonScalingStrokes + '" useScalingStrokes="' + tag.useScalingStrokes + '">');
			indent();
			visitRect(tag.shapeBounds);
			visitRect(tag.edgeBounds);
			visitShape(tag.shapes);
			unindent();
			print('</DefineShape4>');
		}
		
		public override function visitDefineFont2(tag:DefineFont2):void
		{
			// return;
			print('<DefineFont2>');
			indent();
			for each (var shape:Shape in tag.glyphShapeTable) {
				visitShape(shape);
			}
			unindent();
			print('</DefineFont2>');
		}
		
		public override function visitDefineFont3(tag:DefineFont3):void
		{
			// return;
			print('<DefineFont3>');
			indent();
			for each (var shape:Shape in tag.glyphShapeTable) {
				visitShape(shape);
			}
			unindent();
			print('</DefineFont3>');
		}
		
		public override function visitDefineSprite(tag:DefineSprite):void
		{
			print('<DefineSprite character="' + tag.spriteId + '" frames="' + tag.numFrames + '">');
			indent();
			tag.tags.visit(this);
			unindent();
			print('</DefineSprite>');
		}
		
		public override function visitDefineText(tag:DefineText):void
		{
			// return;
			print('<DefineText>');
			indent();
			visitRect(tag.textBounds);
			visitMatrix(tag.textMatrix);
			for each (var record:TextRecord in tag.textRecords) {
				visitTextRecord(record);
			}
			unindent();
			print('</DefineText>');
		}
		
		public function visitTextRecord(record:TextRecord):void
		{
			print('<TextRecord>');
			indent();
			if (record.hasFont) {
				print('<FontId>' + record.fontId + '</FontId>');
			}
			if (record.hasColor) {
				visitRGBA(record.textColor);
			}
			if (record.hasXOffset) {
				print('<x>' + record.xOffset + '</x>');
			}
			if (record.hasYOffset) {
				print('<y>' + record.yOffset + '</y>');
			}
			if (record.hasFont) {
				print('<height>' + record.textHeight + '</height>');
			}
			for each (var entry:GlyphEntry in record.glyphEntries) {
				visitGlyphEntry(entry);
			}
			unindent();
			print('</TextRecord>');
		}
		
		public function visitGlyphEntry(entry:GlyphEntry):void
		{
			print('<GlyphEntry index="' + entry.glyphIndex + '" advance="' + entry.glyphAdvance + '"/>');
		}
		
		public override function visitDefineButton2(tag:DefineButton2):void
		{
			// return;
			print('<DefineButton2 id="' + tag.buttonId + '" trackAsMenu="' + tag.trackAsMenu + '">');
			indent();
			for each (var character:ButtonRecord in tag.characters) {
				visitButtonRecord(character);
			}
			for each (var action:ButtonCondAction in tag.actions) {
				visitButtonCondAction(action);
			}
			unindent();
			print('</DefineButton2>');
		}
		
		public function visitButtonRecord(record:ButtonRecord):void
		{
			// return;
			print('<ButtonRecord character="' + record.characterId + '" depth="' + record.placeDepth + '" hasFilter="' + record.hasFilterList + '" hasBlendMode="' + record.hasBlendMode + '" states="' + getStates(record) + '">');
			indent();
			visitMatrix(record.placeMatrix);
			visitCXFormWithAlpha(record.colorTransform);
			unindent();
			print('</ButtonRecord>');
		}
		
		private function getStates(record:ButtonRecord):String
		{
			var states:Array = [];
			
			if (record.stateHitTest) {
				states.push('hitTest');
			}
			if (record.stateDown) {
				states.push('down');
			}
			if (record.stateOver) {
				states.push('over');
			}
			if (record.stateUp) {
				states.push('up');
			}
			
			return states.join(",");
		}
		
		public function visitActionRecords(records:ActionRecords):void
		{
			print('<ActionRecords>');
			indent();
			for each (var record:ActionRecord in records.actions) {
				print('<Action code="' + record.code + '"/>');
			}
			unindent();
			print('</ActionRecords>');
		}
		
		public function visitButtonCondAction(action:ButtonCondAction):void
		{
			print('<ButtonCondAction keyPress="' + action.condKeyPress + '">');
			indent();
			visitActionRecords(action.actions);
			unindent();
			print('</ButtonCondAction>');
		}
		
		public override function visitDefineMorphShape(tag:DefineMorphShape):void
		{
			// return;
			print('<DefineMorphShape character="' + tag.characterId + '">');
			indent();
			visitRect(tag.startBounds);
			visitRect(tag.endBounds);
			visitMorphFillStyleArray(tag.morphFillStyles);
			visitMorphLineStyles(tag.morphLineStyles);
			/**
			visitShape(tag.startEdges);
			visitShape(tag.endEdges);
			/**/
			unindent();
			print('</DefineMorphShape>');
		}
		
		public function visitMorphFillStyleArray(list:MorphFillStyleArray):void
		{
			print('<MorphFillStyleArray>');
			indent();
			for each (var style:MorphFillStyle in list.fillStyles) {
				visitMorphFillStyle(style);
			}
			unindent();
			print('</MorphFillStyleArray>');
		}
		
		public function visitMorphFillStyle(style:MorphFillStyle):void
		{
			print('<MorphFillStyle type="' + style.fillStyleType + '">');
			indent();
			visitRGBA(style.startColor);
			visitRGBA(style.endColor);
			visitMatrix(style.startGradientMatrix);
			visitMatrix(style.endGradientMatrix);
			visitMorphGradient(style.gradient);
			print('<BitmapId id="' + style.bitmapId + '"/>');
			visitMatrix(style.startBitmapMatrix);
			visitMatrix(style.endBitmapMatrix);
			unindent();
			print('</MorphFillStyle>');
		}
		
		public function visitMorphGradient(gradient:MorphGradient):void
		{
			print('<MorphGradient>');
			indent();
			for each (var record:MorphGradientRecord in gradient.gradientRecords) {
				visitMorphGradientRecord(record);
			}
			unindent();
			print('</MorphGradient>');
		}
		
		public function visitMorphGradientRecord(record:MorphGradientRecord):void
		{
			print('<MorphGradientRecord>');
			indent();
			print('<Start ratio="' + record.startRatio + '">');
			indent();
			visitRGBA(record.startColor);
			unindent();
			print('</Start>');
			print('<End ratio="' + record.endRatio + '">');
			indent();
			visitRGBA(record.endColor);
			unindent();
			print('</End>');
			unindent();
			print('</MorphGradientRecord>');
		}
		
		public function visitMorphLineStyles(styles:MorphLineStyles):void
		{
			print('<MorphLineStyles>');
			indent();
			for each (var style:MorphLineStyle in styles.lineStyles) {
				visitMorphLineStyle(style);
			}
			unindent();
			print('</MorphLineStyles>');
		}
		
		public function visitMorphLineStyle(style:MorphLineStyle):void
		{
			print('<MorphLineStyle>');
			indent();
			print('<Start width="' + style.startWidth + '">');
			indent();
			visitRGBA(style.startColor);
			unindent();
			print('</Start>');
			print('<End width="' + style.endWidth + '">');
			indent();
			visitRGBA(style.endColor);
			unindent();
			print('</End>');
			unindent();
			print('</MorphLineStyle>');
		}
		
		public function visitRect(rect:Rect):void
		{
			print('<Rect minX="' + rect.xMin + '" maxX="' + rect.xMax + '" minY="' + rect.yMin + '" maxY="' + rect.yMax + '"/>');
		}
		
		public function visitRGBA(color:RGBA):void
		{
			print('<RGBA r="' + color.red.toString(16) + '" g="' + color.green.toString(16) + '" b="' + color.blue.toString(16) + '" a="' + color.alpha.toString(16) + '"/>');
		}
		
		public function visitMatrix(matrix:Matrix):void
		{
			print('<Matrix tX="' + matrix.translateX + '" tY="' + matrix.translateY + '" sX="' + matrix.scaleX + '" sY="' + matrix.scaleY + '"/>');
		}
		
		public function visitCXForm(form:CXForm):void
		{
			print('<CXForm>');
			indent();
			print('<Add r="' + form.redAddition + '" g="' + form.greenAddition + '" b="' + form.blueAddition + '"/>');
			print('<Mul r="' + form.redMultiplication + '" g="' + form.greenMultiplication + '" b="' + form.blueMultiplication + '"/>');
			unindent();
			print('</CXForm>');
		}
		
		public function visitCXFormWithAlpha(form:CXFormWithAlpha):void
		{
			print('<CXFormWithAlpha>');
			indent();
			print('<Add r="' + form.redAddition + '" g="' + form.greenAddition + '" b="' + form.blueAddition + '" a="' + form.alphaAddition + '"/>');
			print('<Mul r="' + form.redMultiplication + '" g="' + form.greenMultiplication + '" b="' + form.blueMultiplication + '" a="' + form.alphaMultiplication + '"/>');
			unindent();
			print('</CXFormWithAlpha>');
		}
		
		public function visitShape(shape:Shape):void
		{
			if (shape is ShapeWithStyle) {
				visitFillStyleArray(ShapeWithStyle(shape).fillStyles);
				visitLineStyleArray(ShapeWithStyle(shape).lineStyles);
			}
			for each (var record:ShapeRecord in shape.shapeRecords) {
				switch (record.type) {
					case ShapeRecordTypeConstants.STYLE_CHANGE:
						visitStyleChangeRecord(StyleChangeRecord(record));
						break;
					case ShapeRecordTypeConstants.STRAIGHT_EDGE:
						visitStraightEdgeRecord(StraightEdgeRecord(record));
						break;
					case ShapeRecordTypeConstants.CURVED_EDGE:
						visitCurvedEdgeRecord(CurvedEdgeRecord(record));
						break;
				}
			}
		}
		
		public function visitStyleChangeRecord(record:StyleChangeRecord):void
		{
			print('<StyleChange>');
			indent();
			//*
			print('<stateNewStyles>' + record.stateNewStyles + '</stateNewStyles>');
			/*/
			print('<stateLineStyle>' + record.stateLineStyle + '</stateLineStyle>');
			print('<stateFillStyle0>' + record.stateFillStyle0 + '</stateFillStyle0>');
			print('<stateFillStyle1>' + record.stateFillStyle1 + '</stateFillStyle1>');
			print('<stateMoveTo>' + record.stateMoveTo + '</stateMoveTo>');
			//*/
			if (record.stateNewStyles) {
				visitFillStyleArray(record.fillStyles);
				visitLineStyleArray(record.lineStyles);
			}
			if (record.stateLineStyle) {
				print('<lineStyle>' + record.lineStyle + '</lineStyle>');
			}
			if (record.stateFillStyle0) {
				print('<FillStyle0>' + record.fillStyle0 + '</FillStyle0>');
			}
			if (record.stateFillStyle1) {
				print('<FillStyle1>' + record.fillStyle1 + '</FillStyle1>');
			}
			if (record.stateMoveTo) {
				print('<moveDeltaX>' + record.moveDeltaX + '</moveDeltaX>');
				print('<moveDeltaY>' + record.moveDeltaY + '</moveDeltaY>');
			}
			unindent();
			print('</StyleChange>');
		}
		
		public function visitFillStyleArray(styles:FillStyleArray):void
		{
			print('<FillStyleArray>');
			indent();
			for each (var style:FillStyle in styles.fillStyles) {
				visitFillStyle(style);
			}
			unindent();
			print('</FillStyleArray>');
		}
		
		public function visitFillStyle(style:FillStyle):void
		{
			print('<FillStyle type="' + style.fillStyleType + '">');
			indent();
			visitRGBA(style.color);
			unindent();
			print('</FillStyle>');
		}
		
		public function visitLineStyleArray(styles:LineStyleArray):void
		{
			print('<LineStyleArray>');
			indent();
			for each (var style:LineStyle in styles.lineStyles) {
				if (style is LineStyle2) {
					visitLineStyle2(LineStyle2(style));
				}
				else {
					visitLineStyle(style);
				}
			}
			unindent();
			print('</LineStyleArray>');
		}
		
		public function visitLineStyle(style:LineStyle):void
		{
			print('<LineStyle width="' + style.width + '">');
			indent();
			visitRGBA(style.color);
			unindent();
			print('</LineStyle>');
		}
		
		public function visitLineStyle2(style:LineStyle2):void
		{
			print('<LineStyle2 width="' + style.width + '">');
			indent();
			visitRGBA(style.color);
			unindent();
			print('</LineStyle2>');
		}
		
		public function visitStraightEdgeRecord(record:StraightEdgeRecord):void
		{
			print('<StraightEdgeRecord>');
			indent();
			/*/
			print('<generalLine>' + record.generalLine + '</generalLine>');
			print('<verticalLine>' + record.verticalLine + '</verticalLine>');
			//*/
			if (record.generalLine || record.horizontalLine) {
				print('<deltaX>' + record.deltaX + '</deltaX>');
			}
			if (record.generalLine || record.verticalLine) {
				print('<deltaY>' + record.deltaY + '</deltaY>');
			}
			unindent();
			print('</StraightEdgeRecord>');
		}
		
		public function visitCurvedEdgeRecord(record:CurvedEdgeRecord):void
		{
			print('<CurvedEdgeRecord>');
			indent();
			print('<controlDeltaX>' + record.controlDeltaX + '</controlDeltaX>');
			print('<controlDeltaY>' + record.controlDeltaY + '</controlDeltaY>');
			print('<anchorDeltaX>' + record.anchorDeltaX + '</anchorDeltaX>');
			print('<anchorDeltaY>' + record.anchorDeltaY + '</anchorDeltaY>');
			unindent();
			print('</CurvedEdgeRecord>');
		}
}
}